﻿#region Header

// Written by Tobias Lekman, 2008. For more information and contact details, see http://www.lekman.com.
// 
// File released under Microsoft Public License (Ms-PL). For full details see http://www.microsoft.com/opensource/licenses.mspx
// 
// Conditions and Limitations
// 
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// 
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
// 
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
// 
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
// 
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 

#endregion

#region Using Statements

using System;
using System.Globalization;
using System.Reflection;
using System.Security.Permissions;
using System.Web.UI.WebControls;
using CodePlex.ExtendedLookup.Properties;
using Microsoft.SharePoint;
using Microsoft.SharePoint.ApplicationPages;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;

#endregion

namespace CodePlex.ExtendedLookup
{
    /// <summary>
    /// Code behind class for the web user control of the extended lookup field editor.
    /// </summary>
    public class ExtendedLookupFieldEditor
        : LookupFieldEditor,
          IFieldEditor
    {
        #region Fields

        /// <summary>
        /// The image used to launch the list picker dialog.
        /// </summary>
        protected Image imgListPicker;

        /// <summary>
        /// The text box holding the server relative URL of the list lookup source.
        /// </summary>
        protected TextBox txtListSource;

        /// <summary>
        /// The checkbox used to select if missing values from the view should be added to the dropdown
        /// </summary>
        protected CheckBox cbAddMissingValue;

        /// <summary>
        /// A literal used to 
        /// </summary>
        protected Literal litScriptLiteral;

        /// <summary>
        /// A <see cref="DropDownList"/> used to select the view within the <see cref="LookupFieldEditor.DdlLookupFieldTargetList"/> to filter values by.
        /// </summary>
        protected DropDownList drpViewFilterTargetList;

        #endregion

        #region Methods

        /// <summary>
        /// Register event handler for list picker source list change.
        /// </summary>
        /// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            txtListSource.TextChanged += OnListSourceTextChanged;
        }

        /// <summary>
        /// Ensure that the web and view target lists are bound.
        /// </summary>
        /// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            LabelLookupFieldTargetListTitle.Visible = false;
            DdlLookupFieldTargetList.Visible = false;

            imgListPicker.Attributes.Add("onclick",
                                      String.Format(
                                          CultureInfo.InvariantCulture,
                                          Resources.ToolPickerButtonOnClick,
                                          SPHttpUtility.EcmaScriptStringLiteralEncode(txtListSource.ClientID),
                                          SPHttpUtility.EcmaScriptStringLiteralEncode(SPContext.Current.Web.ServerRelativeUrl)));
            imgListPicker.Attributes.Add("style",
                                      Resources.ToolPickerButtonStyle);

            AddListControlClientScript();
            base.OnPreRender(e);
        }

        /// <summary>
        /// Adds the required JavaScript function for opening the list picker control.
        /// </summary>
        private void AddListControlClientScript()
        {
            litScriptLiteral.Text = String.Format(
                CultureInfo.InvariantCulture,
                Resources.ResourcePickerStringInitialisation,
                string.Empty,
                Resources.OpenPickerJavaScript);
        }

        /// <summary>
        /// Occurs when the list view has changed and ensures that view filter and field binding is data bound.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
        private void OnListSourceTextChanged(object sender,
                                             EventArgs e)
        {
            SPWeb web = SPControl.GetContextWeb(Context);
            SPList list = GetList(web.Site.RootWeb,
                                  txtListSource.Text);
            if (list != null)
            {
                PopulateViewDropDown(drpViewFilterTargetList,
                                     list);
                PopulateShowFieldDropDown(DdlLookupFieldTargetField,
                                          list,
                                          string.Empty);
            }
            else
            {
                txtListSource.Text = string.Empty;
                Page.ClientScript.RegisterClientScriptBlock(GetType(),
                                                            "ListError",
                                                            "alert('Specified list does not exist')",
                                                            true);
            }
            Page.SetFocus(drpViewFilterTargetList);
        }

        /// <summary>
        /// Populates the field selection drop down list.
        /// </summary>
        /// <param name="control">The control to populate.</param>
        /// <param name="list">The list containing the fields.</param>
        /// <param name="selectedField">The name of the currently selected field.</param>
        private static void PopulateShowFieldDropDown(DropDownList control,
                                                      SPList list,
                                                      string selectedField)
        {
            foreach (MethodInfo info in typeof(LookupFieldEditor).GetMethods(BindingFlags.NonPublic | BindingFlags.Static))
            {
                if (info.Name == "PopulateShowFieldDropDown" &&
                    info.GetParameters().Length == 4)
                {
                    info.Invoke(null,
                                new[]
                                    {
                                        (object) control,
                                        list,
                                        list.ID,
                                        selectedField
                                    });
                    break;
                }
            }
        }

        /// <summary>
        /// Gets the list from the server relative URL of the list.
        /// </summary>
        /// <param name="rootWeb">The root web to start looking within.</param>
        /// <param name="url">The server relative URL of the list to retrieve.</param>
        /// <returns>The list instance corresponding to the specified server relative URL.</returns>
        private static SPList GetList(SPWeb rootWeb,
                                      string url)
        {
            SPWeb web = rootWeb;
            foreach (string part in url.Split(new[] { '/' },
                                              StringSplitOptions.RemoveEmptyEntries))
            {
                foreach (SPList list in web.Lists)
                {
                    if (list.Title == part)
                    {
                        return list;
                    }
                }
                foreach (SPWeb subWeb in web.Webs)
                {
                    if (subWeb.Name == part)
                    {
                        web = subWeb;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Populates a <see cref="DropDownList"/> with views from a list.
        /// </summary>
        /// <param name="dropDownList">The <see cref="DropDownList"/> to populate.</param>
        /// <param name="list">The list containing the views to populate with.</param>
        private static void PopulateViewDropDown(ListControl dropDownList, SPList list)
        {
            dropDownList.Items.Clear();
            foreach (SPView view in list.Views)
            {
                if (!view.Hidden)
                {
                    dropDownList.Items.Add(new ListItem(view.Title,
                                                        view.ID.ToString("B")));
                }
            }
            if (dropDownList.Items.Count == 0)
            {
                dropDownList.Items.Add(new ListItem(list.DefaultView.Title,
                                                    list.DefaultView.ID.ToString("B")));
            }
            else
            {
                dropDownList.SelectedValue = list.DefaultView.ID.ToString("B");
            }
        }

        #endregion

        #region Implementation of IFieldEditor

        /// <summary>
        /// Initializes the field property editor when the page loads.
        /// </summary>
        /// <param name="field">An object that instantiates a custom field (column) class that derives from the <see cref="SPField"/> class. </param>
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public new void InitializeWithField(SPField field)
        {
            base.InitializeWithField(field);
            ExtendedLookupField lookupField = field as ExtendedLookupField;
            if (lookupField != null &&
                !string.IsNullOrEmpty(lookupField.LookupList) &&
                string.IsNullOrEmpty(txtListSource.Text))
            {
                using (SPWeb web = SPContext.GetContext(Context).Site.OpenWeb(lookupField.LookupWebId))
                {
                    SPList list = web.Lists[new Guid(lookupField.LookupList)];
                    txtListSource.Text = list.RootFolder.ServerRelativeUrl;
                    txtListSource.Enabled = false;
                    imgListPicker.Visible = false;
                    PopulateViewDropDown(drpViewFilterTargetList,
                                         list);
                    drpViewFilterTargetList.SelectedValue = lookupField.LookupViewId.ToString("B");
                    PopulateShowFieldDropDown(DdlLookupFieldTargetField,
                                              list,
                                              lookupField.LookupField);
                    cbxAllowMultiValue.Checked = lookupField.AllowMultipleValues;
                    cbAddMissingValue.Checked = lookupField.AddMissingValue;
                }
            }
        }

        /// <summary>
        /// Validates and saves the changes the user has made in the field property editor control.
        /// </summary>
        /// <param name="field">The field (column) whose properties are being saved.</param>
        /// <param name="isNewField">true to indicate that the field is being created; false to indicate that an existing field is being modified.</param>
        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public new void OnSaveChange(SPField field,
                                     bool isNewField)
        {
            SPFieldLookup lookup = (SPFieldLookup)field;
            ExtendedLookupField extended = (ExtendedLookupField)field;

            if (isNewField)
            {
                if (drpViewFilterTargetList.SelectedValue != null)
                {
                    extended.Save(new Guid(drpViewFilterTargetList.SelectedValue),
                                  cbxAllowMultiValue.Checked, cbAddMissingValue.Checked);
                }
                SPList list = GetList(SPContext.Current.Site.RootWeb,
                                      txtListSource.Text);
                lookup.LookupList = list.ID.ToString("B");
                lookup.LookupWebId = list.ParentWeb.ID;
                lookup.LookupField = DdlLookupFieldTargetField.SelectedValue;
            }
            else
            {
                extended.LookupViewId = new Guid(drpViewFilterTargetList.SelectedValue);
                extended.AllowMultipleValues = cbxAllowMultiValue.Checked;
                extended.AddMissingValue = cbAddMissingValue.Checked;
            }
        }

        #endregion
    }
}